<?PHP
/*   This is a BSD style permissive license.
*    This module is original work by the author.
*
* Copyright (c) 2006-2007, iNetOffice, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of iNetOffice nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY iNetOffice ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL iNetOffice BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Tom Snyder, iNetOffice, Inc.
*/


/* -- opensam_browse_webdav.php --
 *
 * Implements a WebDAV file and folder browsing UI presented through a web browser via
 * a <FRAME> or <IFRAME>. Uses the OpenSAM WebdavClient.php utility routines to
 * communicate with the Webdav server.
 *
 * The <IFRAME> served by this code would generally be in a File Open, File Save, ...
 * type dialog box. The user traverses and WebDAV stored folder hierarchy using
 * this code. When they found the location of file they want, they click the filename
 * which calls back to the container application to inform them the filename was
 * selected.
 *
 * ****** INTEGRATING ********************************************************************
 *
 *  To integrate the module with your server, do the following:
 *
 *   1. Define 'contexts' for instances of this WebDAV browser IFRAME. A context is simply a short
 *      string that identifies which dialog box or which file area a specific WebDAV browsing
 *      IFRAME is associated with. The Context keeps track of the current working directory.
 *
 *   2. Create a FileClickedCallback( contextstring, filepathname ) JavaScript routine in the
 *      parent window of the IFRAME this runs in. That callback is called when the user clicks on
 *      a filename. It usually begins the file open, picture insert, etc process.
 *
 *   3. Examine and customize the code just below in the ### CUSTOMIZE HERE ### section to
 *      provide the UI details, user authentication, and other integration points this module needs to
 *      operate within your services.
 *
 *   4. Run it and enjoy!
 *
 * ****************************************************************************************
 *
 * CONTENT RATIONALE:
 *  The file and folder list is arranged to have a similar feel to that of desktop
 *  applications. The list of folders is duplicated, 1st in a select at the top and
 *  2nd as an inline list at the bottom. This gives quick no-scroll access to both
 *  files and folders, and also ensures a novice user that never clicks the folder
 *  select can quickly discover the full list of folders.
 *
 *  The folders <select> at the top also gives the breadcrumbs style full hierarchy
 *  of the current folder's location and offers direct access to any parent folder.
 *  
 * CGI STRUCTURE:
 *  This code communicates with itself via CGI requests back here. Although all state
 *  information is fully communicated back each time, we also cache the 'current folder'
 *  in a session variable so when the user launches the dlg again they are in the same
 *  place they left off. For this reason there is also a "Context" string associated
 *  with each webdav browse windows. This lets you have different default Current Working
 *  Directory for File Open and Picture Insert, for example.
 *
 * NAMESPACE MANAGEMENT:
 *  All global symbols in this module are prefixed with SBW_ so that you can freely intermingle
 *  it with your other modules.
 *
 *
 *  HOW TO INTERFACE
 *   
 *  CGI PARAMETERS
 *  This request processor accepts these CGI parameters:
 *
 *   Context=open
 *    -- the context or type of this folder browse operations. This allows you to use this
 *       same module in several different dialog boxes and keep their current-working-diretories
 *       distinct.
 *
 *   Action=
 *    -- the action to perform. Often left blank to simply show the files and folder in the
 *       current-working-directory.   
 *
 *   FolderPlusFileName=
 *    -- The full path and filename of a file to operate on.
 *
 *   FolderPath=
 *    -- The full path of a folder to display or operate on.
 *
 *
 *   SESSION VARIABLES
 *
 *   StorageUserName
 *   StorageSessionId
 *   StoragePassword
 *    -- The OpenSAM WebDAV authentication credentials.
 *
 *
 */
 
 
/* ################################################################################
 * ################################################################################
 * ###################  CUSTOMIZE BELOW HERE  ##################################### 
 */

/*
 ******* LOOK AND FEEL DECLARARIONS ***************************************
 *
 * (These are variables rather than defines to allow other configuration components on
 *  your server to modify them).
 */
 
$SBW_UI_NoFiles_String       = "[ no files ]";   // displayed when a folder has no files or subfolders.

$SBW_UI_TopFolder_String     = "--Top Folder--"; // must be string since it is in a list box.
$SBW_UI_FolderIcon_HTML      = "<img src=\"ico_folder.gif\">";  // the folder icon next to folder names.
$SBW_UI_FolderGoUpIcon_HTML  = "<img src=\"ico_folderup.gif\">";// the go-up folder icon.
$SBW_UI_DeleteButton_HTML    = "del";    // if blank, no delete button will be shown.

$SBW_UI_ColorBackground_AttrValue  = "#FFFFFF";

// Put all the CSS formatting to make the text look as you wish:
$SBW_UI_DefaultText_CSS = "cursor: default; font-family: arial; font-size: 8pt;";

// SPACING of elements:
// Outside margins:
$SBW_UI_Margins_CSSValue  = "6px;";
$SBW_UI_Margins_PXValue  = "6";

// Spacing between sections (dropdowns, files list, folders list):
$SBW_UI_Spacing_FolderIconToText_CSSValue = "4px;";
$SBW_UI_Spacing_FileNameToDel_CSSValue = "10px;";
$SBW_UI_Spacing_VerticalSections_CSSValue = "6px;";

// Globbing Filters.
// Each context (that you've defined) can have a set of filename extensions
// associated with it. Only files with those extensions will be displayed. If a
// context has no such globbing filter defined, then all filenames will be
// displayed. The globbing filter is a regular expression to match the filename.
//
$SBW_CTX_GLOBBING_FILTERS = array( 'ctx' => "\\.html?$",
                                   'ctx2' => "\\.xls$",
                                   'images' => "\\.(jpg|gif|png)$" );


/* ----> -----> ----> -----> ----> -----> ---->
 *     WEBDAV MODULES, USER AUTHENTICATION
 * ----> -----> ----> -----> ----> -----> ---->
 * import the webdav client modules from where you stored them.
 * validate the user is logged in and redirect to a login page if not.
*/

require_once( "PEAR.php" );   // Webdav support code.
require_once( "dateTime.php" );
require_once( "Socket.php" );
require_once( "URL.php" );
require_once( "OurRequest.php" );
require_once( "WebdavClient.php" );

session_start();

if( empty( $_SESSION['StorageUserName'] ) ) {
  SBW_print_error_and_exit( "You are not logged in." );
}  

// Make sure we have enough to go on:
if( empty( $_SESSION['StorageUserName'] )
 || (empty( $_SESSION['StorageSessionId'] ) 
     && empty( $_SESSION['StoragePassword'] )) ) {
  SBW_print_error_and_exit( "WebDAV credentials not found in session data." );
}

// Prepare WebDAV Client Object:
$WEBDAV_obj = new HTTP_WebDAV_Client;

// If WebDAV password, then put in the HTTP authenticate headers. Otherwise
// the WebDAVUrlParameters session var has the authentication values.
if( !empty( $_SESSION['StoragePassword'] ) ) {
  $WEBDAV_obj->set_user( $_SESSION['StorageUserName'] );
  $WEBDAV_obj->set_pass( $_SESSION['StoragePassword'] );
}
else if( empty( $_SESSION['WebDAVUrlParameters'] ) ) {
  SBW_print_error_and_exit( "WebDAV WebDAVUrlParameters credentials not found in session data." );
}
  
/* ###################  CUSTOMIZE ABOVE HERE  ##################################### 
 * ################################################################################
 * ################################################################################
 */

 
/* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 *     CGI PARAMETERS
 *  We accept CGI parameters as either POST or GET for maximum flexibility.
 * &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 */

$SBW_CGI_Context            = SBW_helper_get_CGI( "Context" );            // ALWAYS provided.
$SBW_CGI_Action             = SBW_helper_get_CGI( "Action" );             // For verbs such as delete.
$SBW_CGI_FolderPlusFileName = SBW_helper_get_CGI( "FolderPlusFileName" ); // For verbs such as delete.
$SBW_CGI_FolderPath         = SBW_helper_get_CGI( "FolderPath" );         // For browsing files.

// VALIDATE REQUEST:
if( empty( $SBW_CGI_Context ) ) {
  SBW_print_error_and_exit( "Invalid Request. The 'Context' CGI parameter must be supplied." );
}


// Current Folder Mainenance:
$SBW_Current_FolderPath = "/";  // a safe default. All processing results in a valid value in this variable.

// If a folder name is given them make it the current one.
// Otherwise get the current one to use.
if( empty( $SBW_CGI_Context ) ) $SBW_CGI_Context = "";  // so it doesn't get in the way.
if( !empty( $SBW_CGI_FolderPath ) ) {
  $SBW_Current_FolderPath = $_SESSION['SBW_FOLDERCWD_' . $SBW_CGI_Context ] = $SBW_CGI_FolderPath;
}
else {
  // If none, then use the session variable, if any:
  if( array_key_exists( 'SBW_FOLDERCWD_' . $SBW_CGI_Context, $_SESSION ) ) {
    $SBW_Current_FolderPath = $_SESSION['SBW_FOLDERCWD_' . $SBW_CGI_Context ];
  }
}


$SBW_full_folder_url = SBW_helper_assemble_path_components( $_SESSION['StorageServerUrl'], 
  $SBW_Current_FolderPath );
  
$SBW_full_folder_url_encoded = SBW_helper_assemble_path_components( $_SESSION['StorageServerUrl'], 
  SBW_helper_urlencode_path_components( $SBW_Current_FolderPath ) );

$SBW_full_folder_url_encoded_plus_optional_SSO_params = 
	SBW_helper_add_optional_SSO_params( $SBW_full_folder_url_encoded );

// Diagnostic:  
//print "Full Folder: -$SBW_full_folder_url_encoded-<br>";
//print "Full with optional params:: -$SBW_full_folder_url_encoded_plus_optional_SSO_params-<br>";

/* ----> ------>----> ------>---->
 *     ACTION DISPATCH
 * ----> ------>----> ------>---->
*/
switch( $SBW_CGI_Action ) {
  case "delete":  // wants to delete 
    // Fill in your delete code here.
    break;
  
  default:
    break; // this is OK. The browse code does not use the "Action" CGI param.
           // the browse is accomplished by setting the FolderFileName and simply
           // re-rendering the page.
}    

// Get the structured list of files and folders from the webdav server:
//
$SBW_folder_contents = $WEBDAV_obj->dir_ls( $SBW_full_folder_url_encoded_plus_optional_SSO_params );

if( !$SBW_folder_contents ) {
  // ERROR: report the error and exit.
  SBW_print_error_and_exit( "FAILURE: " . $WEBDAV_obj->errmessage . "(".$WEBDAV_obj->reqresponse.")" );
}
// Clean off some URL prefix cruft WebDAV might leave:
SBW_helper_clean_dirls_filenames( $SBW_CGI_Context, $SBW_full_folder_url, $SBW_folder_contents );
// Sort alphabetically for easy display:
usort( $SBW_folder_contents, "SBW_helper_cmpfunc_dirls_filenames" );

// Render the page for this folder:
SBW_print_wholepage( $SBW_CGI_Context, $SBW_Current_FolderPath, $SBW_folder_contents );


exit;

// -------------------------------------- END OF GLOBALLY EXECUTED CODE ------------------------------------


// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> HTML GENERATION <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>

// Top level routine prints the page in its entirety.
//
// the $folder_contents param is by reference for efficiency. This way PHP does not
// have to duplicate the whole thing.
//  RETURNS:  nothing.
function SBW_print_wholepage( $Context, $FolderPath, &$folder_contents ) {
  SBW_print_html_header( $Context, $FolderPath  );
  SBW_print_html_body( $Context, $FolderPath, $folder_contents );
  SBW_print_html_end( $Context, $FolderPath );
}

// The body contains:
//   SELECT - the dropdown select of available folders.
//   FILE LIST - a table containing the list of files in the current folder.
//   FOLDER LIST - a table containing the list of folders in the current folder.
//
//  RETURNS: nothing.

function SBW_print_html_body( $Context, $FolderPath, &$folder_contents ) {
  global $SBW_UI_MarginsLeftRight_CSSValue, $SBW_UI_Margins_PXValue;
  global $SBW_UI_ColorBackground_AttrValue;

    // This set of margin attributes works on both IE and Firefox:
  print "<BODY topmargin=$SBW_UI_Margins_PXValue leftmargin=$SBW_UI_Margins_PXValue ".
        " rightmargin=$SBW_UI_Margins_PXValue bottommargin=$SBW_UI_Margins_PXValue ".
        " marginwidth=$SBW_UI_Margins_PXValue marginheight=$SBW_UI_Margins_PXValue ".
        " bgcolor=\"$SBW_UI_ColorBackground_AttrValue\">";
  
  SBW_print_folders_select( $Context, $FolderPath, $folder_contents );
  SBW_print_files_table( $Context, $FolderPath, $folder_contents );
  SBW_print_folders_table( $Context, $FolderPath, $folder_contents );
  
  print "</BODY>";
}

//
// Print the folders <select> and related stuff on the 1st line of page.
//
//  RETURNS: nothing.
function SBW_print_folders_select( $Context, $FolderPath, &$folder_contents ) {
  global $SBW_UI_FolderIcon_HTML, $SBW_UI_FolderGoUpIcon_HTML;
  global $SBW_UI_TopFolder_String, $SBW_UI_Spacing_VerticalSections_CSSValue;
  
  // This first line we print is in its own DIV:
  print "<DIV align=center style=\"padding-bottom: $SBW_UI_Spacing_VerticalSections_CSSValue;\">";  

  //  FOLDER UP ICON. Users click it to ascend up one level. It lacks the up arrow if
  //                  they are at the top folder thus re-inforcing the UI for that state.
  //
  // Find the full parent-folder sequence for this folder:
  $folderstack = SBW_helper_get_folder_stack( $FolderPath );
  
  // Add the folder icon to the left which serves as an up-directory control:
  if( empty($folderstack) ) {  
    // WE ARE A TOP FOLDER
    $folderico = $SBW_UI_FolderIcon_HTML;
    $linktag = $linkendtag = "";
  } else {
    $folderico = $SBW_UI_FolderGoUpIcon_HTML;
    $folder_up_one = SBW_helper_remove_trailing_slash( dirname( $FolderPath ) );
    if( empty( $folder_up_one ) || $folder_up_one == ".") $folder_up_one = "/";  // root is always represented by a /, never an empty string.
    $linktag = "<SPAN onclick=\"fldrclk('$folder_up_one',1);\">";
    $linkendtag = "</SPAN>";
  }
  print "$linktag$folderico$linkendtag&nbsp; ";
  
  // --SELECT TAG
  print "<SELECT id=selFolders name=SELfolders style=\"font-size: 8pt;\" onchange=\"fldrsel()\">\n";
    
  // --TOP FOLDER:
  // The special "Top Folder" location explicitly anchors everything for the user:
  $select_this = empty($folderstack) ? " selected" : "";
  $SBW_UI_TopFolder_String_escaped = htmlentities( $SBW_UI_TopFolder_String );
  print "<OPTION value=\"/\"$select_this>$SBW_UI_TopFolder_String_escaped</OPTION>\n";
   
  $pluses = "";
  $spaces = "&nbsp;&nbsp;";
  $full_path_encoded = "";
  // --ANCESTORS PATH:
  // Put in the path entries down to the current folder:  
  //  the ancestor path is given first, each level having an additional +.
  // $folders_dropdown_data gives us the entire folders dropdown data in a structured format. We take
  // that structured info and build the <select> <options> and stuff.
  $lastindex = count( $folderstack )-1;
  foreach( $folderstack as $i => $folderitem ) {
    $pluses .= "+";
    $spaces .= "&nbsp;&nbsp;";
    $filename_encoded = htmlentities( $folderitem );
    $full_path_encoded .= $filename_encoded;
    $select_this = (($i == $lastindex) ? " selected" : "");
    print "<OPTION value=\"$full_path_encoded\"$select_this>$pluses$filename_encoded</OPTION>\n";
    $full_path_encoded .= "/";
  }
  
  // --FOLDERS WITHIN THIS FOLDER:
  foreach( $folder_contents as $folderitem ) {
    if( $folderitem['fcollection'] ) {
      // Is a folder:
      $foldername_esc = htmlentities( $folderitem['filename'] );
      print "<OPTION value=\"$full_path_encoded$foldername_esc\">$spaces$foldername_esc</OPTION>\n";
    }
  }

  print "</SELECT></DIV>";  
}

// The list of files is placed in a table so that icons or buttons can be put to the left
// or the right of filenames and the filenames always line up.
// Right now this is just the del link.
//  RETURNS: nothing.
function SBW_print_files_table( $Context, $FolderPath, &$folder_contents ) {
  global $SBW_UI_Spacing_VerticalSections_CSSValue;
  global $SBW_UI_NoFiles_String;
  
  print "<DIV align=center style=\"padding-bottom: $SBW_UI_Spacing_VerticalSections_CSSValue;\">";  

  // For completely empty folder just print a simple msg: 
  if( count( $folder_contents ) == 0 ) {
    print "$SBW_UI_NoFiles_String</DIV>\n";
    return;
  }
  
  // We accumulate the table rows in a string since if there are none we don't want to put
  // out the table at all:
  $trows = "";
  $folderpath_esc = htmlentities( $FolderPath );
	// For the root folder we want nothing there:
	if( $folderpath_esc == "/" ) $folderpath_esc = "";

  foreach( $folder_contents as $folderitem ) {
    if( !$folderitem['fcollection'] ) {
      // Is a file:
      $filename_esc = htmlentities( $folderitem['filename'] );
      $trows .= "<TR><TD><SPAN onclick=\"fclk('$folderpath_esc/$filename_esc')\">$filename_esc</SPAN></TD>";
      $trows .= "<TD class=fdel><SPAN onclick=\"fdel('$folderpath_esc/$filename_esc')\">del</SPAN></TD></TR>\n";
    }
  }
  
  if( $trows == "" ) return;
  
  print "<TABLE cellpadding=0 cellspacing=0 border=0 unselectable=on>\n";
  print $trows;
  print "</TABLE></DIV>\n";
}

// The list of folders is placed in a table so that the folders icons to the left
// of foldernames always line up.
// Right now this is just the del link.
//  RETURNS: nothing.
function SBW_print_folders_table( $Context, $FolderPath, &$folder_contents ) {
  global $SBW_UI_FolderIcon_HTML;
  global $SBW_UI_Spacing_VerticalSections_CSSValue, $SBW_UI_Spacing_FolderIconToText_CSSValue;
  
  print "<DIV align=center style=\"padding-bottom: $SBW_UI_Spacing_VerticalSections_CSSValue;\">";  
  
  // We accumulate the table rows in a string since if there are none we don't want to put
  // out the table at all:
  $trows = "";
  foreach( $folder_contents as $folderitem ) {
    if( $folderitem['fcollection'] ) {
      // Is a folder:
      $foldername_esc = htmlentities( $folderitem['filename'] );
      $onclick_attr = "onclick=\"fldrclk('$foldername_esc')\"";
      // Note: we use the onclick for the whole TD since the folder icon is the same size in every row.
      //  We surround the folder name with a SPAN so that they must slick on the actual foldername text.
      //  If we use the TD there then clicking on a black area would take you to that folder too, a little
      //  disconcerting.
      $trows .= "<TR><TD class=fldr $onclick_attr>$SBW_UI_FolderIcon_HTML</TD><TD><SPAN $onclick_attr>$foldername_esc</SPAN></TD></TR>\n";
    }
  }
  
  if( $trows == "" ) return;
  
  print "<TABLE cellpadding=0 cellspacing=0 border=0 unselectable=on>\n";
  print $trows;
  print "</TABLE></DIV>\n";
}

//  RETURNS: nothing.
function SBW_print_html_header( $Context, $FolderPath ) {
  global $SBW_UI_DefaultText_CSS;
  global $SBW_UI_Spacing_FileNameToDel_CSSValue, $SBW_UI_Spacing_FolderIconToText_CSSValue;
  
  // process folderpath so it can print correctly even with special chars such as & or <.
  $FolderPath_can_render_in_html = htmlentities( $FolderPath );
  // Put the folderpath in the Title just for kicks. Will not usually be shown since we are typically in 
  // a FRAME or IFRAME.
  print "<HTML><HEAD><TITLE>$FolderPath_can_render_in_html</TITLE>\n";
  print "<STYLE>\n";
  print "IMG { cursor: hand; }\n";
  print "SPAN { cursor: hand; };\n";
  print "TD, DIV { $SBW_UI_DefaultText_CSS }\n";
  print "TD.fldr { padding-right: $SBW_UI_Spacing_FolderIconToText_CSSValue; }\n";
  print "TD.fdel { padding-left:  $SBW_UI_Spacing_FileNameToDel_CSSValue; } \n";
  print "</STYLE>\n";
  SBW_print_script_block( $Context, $FolderPath ); 
  print "</HEAD>\n";
}

//  RETURNS:  nothing.
function SBW_print_script_block( $Context, $FolderPath ) {
  // for the path combinations to work we do not want
  // $FolderPath to be just /
  if( $FolderPath == "/" ) $FolderPath = "";
  else $FolderPath .= "/";  // for a trailing slash.
  $FolderPath_encoded = urlencode( $FolderPath );
?>

<SCRIPT type="text/javascript">
function fclk( filepath ) {
  if( !window.parent.FileClickedCallback ) {
    alert( 'Please write a FileClickedCallback() routine for me to call.' );
    return;
  }
  window.parent.FileClickedCallback( <?PHP print "'$Context'" ?>, filepath );
}
function fldrclk( folderpath, dontadd ) {
  if( dontadd ) {
<?PHP
    // passed in path is the full path:
    print "document.location='?Context=$Context&FolderPath='+folderpath;\n";
    print "} else {\n";
    print "document.location='?Context=$Context&FolderPath=$FolderPath_encoded'+folderpath;\n";
?>
  }
}
function fldrsel() {
  var selElement = document.getElementById( "selFolders" );
  fldrclk( selElement.value, 1 );
}
function fdel( filepath ) {
  if( confirm( "Are you certain you'd like to delete "+filepath+"?" ) ) {
    if( !window.parent.FileDelCallback ) {
      alert( 'Please write a FileDelCallback() routine for me to call.' );
      return;
    }
    window.parent.FileDelCallback( filepath );
  }
}
</SCRIPT>

<?PHP
}

//  RETURNS: nothing.
function SBW_print_html_end( $Context, $FolderPath ) {
  print "</HTML>";
}

// -------------------------------------- MISC HELPER ROUTINES  ------------------------------------

// Return the value of the param. If none, then return "". This maps well to the several cases since
// people writing links witll probably consider pname=&pother=blah to be the same as leaving pname off
// the list altogether.
//  RETURNS: string CGI parameter value, from _GET or _POST.
function SBW_helper_get_CGI( $paramname ) {
  // pre-testing with array_key_exists() prevents PHP generated warnings from filling you log files.
  if( array_key_exists( $paramname, $_GET ) ) {
    return( $_GET[$paramname] );
  }
  if( array_key_exists( $paramname, $_POST ) ) {
    return( $_POST[$paramname] );
  }
  return( "" );  // no such parameter.
}

// An array of parent folders is useful when emitting
// a folder's context:
//  RETURNS: array of path components of ancestor folders of the pathname.
function SBW_helper_get_folder_stack( $pathname ) {
  $retarr = array();
  
  // Sorry, this test provides maximum PHP compatibility.
  while( !empty( $pathname ) && $pathname != '/' && $pathname != '.' ) {
    $basedir = basename( $pathname );
    $pathname  = dirname( $pathname );
    array_push( $retarr, $basedir );
  }
  return( array_reverse( $retarr ) );
}

// Different WebDAV servers return a PROPFIND's returned filenames differently.
// Some return just the plain filename (as you would expect), others return
// the whole path from the root of the webdav location.
//  RETURNS:  NOTHING. $folder_contents is modified in place. 
function SBW_helper_clean_dirls_filenames( $Context, $url_prefix, &$folder_contents ) {
  global $SBW_CTX_GLOBBING_FILTERS;
  
  $url_parsed = parse_url($url_prefix);
  $path_prefix = $url_parsed['path'];
  
  // normalize the path prefixes to no-slash for uniformity:
  $path_prefix = SBW_helper_remove_leading_slash( $path_prefix );
  
  foreach( $folder_contents as $indx => $folderitem ) {
    if( !empty( $folder_contents[$indx]['filename'] ) ) {
      // normalize the filepath path to no-slash for uniformity:
      $folder_contents[$indx]['filename'] = SBW_helper_remove_leading_slash( $folder_contents[$indx]['filename'] );
      
      // Remove the path prefix if it exists there:
			$folder_contents[$indx]['filename'] = SBW_helper_remove_prefix_if_there( $path_prefix, $folder_contents[$indx]['filename'] );
			$folder_contents[$indx]['filename'] = SBW_helper_remove_trailing_slash( $folder_contents[$indx]['filename'] );
			$folder_contents[$indx]['filename'] = SBW_helper_remove_leading_slash( $folder_contents[$indx]['filename'] );
			
			// Webdav servers may include the current folder in the list of folders (ouch):
			if( $folder_contents[$indx]['filename'] == "" ) unset( $folder_contents[$indx] );
			
			// If there is a globbing filter defined, then apply it:
			else if( !$folder_contents[$indx]['fcollection']
			 && array_key_exists( $Context, $SBW_CTX_GLOBBING_FILTERS ) 
			 && !eregi( $SBW_CTX_GLOBBING_FILTERS[$Context], $folder_contents[$indx]['filename'] ) ) {
			    unset( $folder_contents[$indx] );
			}
    }
  }
}

// Help sort the results of our DirLS call, case insensitive alphabetical:
//  RETURNS: -1, 0, or 1 depending on the sort order of dirls_item1 and dirls_item2.
function SBW_helper_cmpfunc_dirls_filenames( $dirls_item1, $dirls_item2 ) {
  return( strcasecmp( $dirls_item1['filename'], $dirls_item2['filename'] ) );
}

// We accept a pathname as a straight string:
//   "FolderName/Sub Folder/Bottom File"
// It is all URLEncoded when passed to us, and completely uncoded when we get it
// from the _GET params.
//
// Here's the problem: when we build a WebDAV URL, we want the components in the path
// to be encoded (spaces, special chars, etc), but NOT the slashes that seperate the
// path components. Thus that sample string should become:
//   "FolderName/Sub%20Folder/Bottom%20File"
//  RETURNS: path as a string with components encoded.
function SBW_helper_urlencode_path_components( $pathname ) {
  $paths_array = SBW_helper_get_folder_stack( $pathname );
  $retpath = "";
  foreach( $paths_array as $pathcomponent ) {
    $retpath .= rawurlencode( $pathcomponent ) . "/";
  }
  return( SBW_helper_remove_trailing_slash( $retpath ) );
}

// Assemble two path components together ensuring there is exactly one '/' between
// them.
//  RETURNS: $comp1 and $comp2 concatenated with a single / between them.
function SBW_helper_assemble_path_components( $comp1, $comp2 ) {
  // remove any trailing / from comp1:
  $comp1 = SBW_helper_remove_trailing_slash( $comp1 );
  // remove any leading  / from comp2:
  $comp2 = SBW_helper_remove_leading_slash( $comp2 );
  
  return( $comp1 . '/' . $comp2 );
}

// For SalesForce integration we need to add some magical params
// to the WebDAV URL:
function SBW_helper_add_optional_SSO_params( $SBW_full_folder_url_encoded ) {
	if( empty( $_SESSION['WebDAVUrlParameters'] ) ) return( $SBW_full_folder_url_encoded );
	return( $SBW_full_folder_url_encoded . $_SESSION['WebDAVUrlParameters'] );
}


//  RETURNS: $path with any leading / removed.
function SBW_helper_remove_leading_slash( $path ) {
  return( ereg_replace( "^/", "", $path ) );
}
//  RETURNS: $path with any trailing / removed.
function SBW_helper_remove_trailing_slash( $path ) {
  return( ereg_replace( "/$", "", $path ) );
}

//  RETURNS: $string with $prefix removed if it is found at the beginning.
function SBW_helper_remove_prefix_if_there( $prefix, $string ) {
	if( !strncmp( $prefix, $string, $thelen = strlen( $prefix ) ) ) {
		$string = substr( $string, $thelen );
	}
	return( $string );
}

// Darn. Something went wrong. Report the error message and exit completely.
//  RETURNS: nothing. Does not return.
function SBW_print_error_and_exit( $ErrorMessage_HTML ) {
  print "<HTML><HEAD><TITLE>Error - File Browser</TITLE></HEAD>\n";
  print "<BODY><P>$ErrorMessage_HTML</P></BODY></HTML>";
  exit( 1 );  
}

?>
